#!/usr/local/bin/python3
# -*- coding: utf-8 -*-

"""
@File    : analysis_core_test.py
@Author  : Link
@Time    : 2022/12/16 21:24
@Mark    :
"""
import pickle
import sys
import unittest

import pandas as pd
from PySide2.QtWidgets import QApplication

from app_test.test_utils.mixins import Hdf5DataLoad
from app_test.test_utils.wrapper_utils import Tester
from app_test.test_utils.log_utils import Print
from chart_core.chart_pyqtgraph.ui_components.chart_trans_bar import TransBarChart
from common.app_variable import TestVariable, ReadFail
from common.cal_interface.capability import CapabilityUtils
from common.li import FutureLi, Li
from parser_core.stdf_parser_file_write_read import ParserData
from parser_core.stdf_parser_func import DtpOptFlag, PrrPartFlag


class ReadHdf5BaseAnalysisCase(unittest.TestCase, Hdf5DataLoad):
    """
    read hdf5 file with base analysis
    query占用的时间比较长,尽量不要用,快速验证可以用
    DA_GROUP
    TODO: 单个数据单元的测试, UI中组合的会复杂一些
        @20230107:
            1. 先转为通用格式
            2. 每个子数据使用unstack进行转置
            3. 更新最新的列名
    """
    top_fail_dict = None

    @Tester(
        ["load_data"],
        exec_time=True,
    )
    def test_load_data(self):
        """
        测试Read Hdf5数据
        :return:
        """
        select_summary, id_module_dict = self.summary.load_select_data([1, 2])
        self.li.set_data(select_summary, id_module_dict)
        self.li.concat()

    @Tester(
        ["load_data"],
        exec_time=True,
    )
    def test_print_base_data(self):
        """

        :return:
        """

        " 测试能否根据 TEST_ID/测试项目 这个测试项目的所有数据 "
        df = self.df_module.dtp_df[self.df_module.dtp_df.TEST_ID == 1]
        print(df)

        " 测试是否可以识别测试数据的Only Pass "
        pass_df = self.df_module.prr_df.query(
            """
            FAIL_FLAG == 1
            """
        )
        print(df.query(
            """
            index in @pass_df.index
            """
        ))
        pass_qty = len(pass_df)
        Print.Success("PassQty:{}".format(pass_qty))

        " 测试是否可以识别测试数据的Only Fail "
        fail_df = self.df_module.prr_df.query(
            """
            FAIL_FLAG != 1
            """
        )
        print(df.query(
            """
            index in @fail_df.index
            """
        ))
        fail_qty = len(fail_df)
        Print.Success("FailQty:{}".format(fail_qty))

        all_qty = pass_qty + fail_qty
        Print.Success("AllQty:{}".format(all_qty))

        " 测试数据是否可以被解析为动态PAT "
        if len(df) != len(df[df.OPT_FLAG & DtpOptFlag.PatValid == DtpOptFlag.PatValid]):
            Print.Warning("No Pat Data")
        else:
            Print.Success("Can Get Pat Data")
        return True

    @Tester(
        ["test_load_data"],
        exec_time=True,
    )
    def test_print_first_re_finally_test(self):
        """
        测试识别初测和复测的数据
        :return:
        """
        first_df = self.df_module.prr_df[
            self.df_module.prr_df.PART_FLG & PrrPartFlag.FirstTest != PrrPartFlag.FirstTest
            ]
        Print.Success("FirstTestQty:{}".format(len(first_df)))
        retest_df = self.df_module.prr_df[
            self.df_module.prr_df.PART_FLG & PrrPartFlag.FirstTest == PrrPartFlag.FirstTest
            ]
        Print.Success("RetestTestQty:{}".format(len(retest_df)))

        """
        finally_df:
            1. get first_df pass
            2. get retest all
            3. concat 1&2
        """
        first_pass_df = first_df.query(
            """
            FAIL_FLAG == 1
            """
        )
        finally_df = pd.concat([first_pass_df, retest_df])
        Print.Success("FinallyTestQty:{}".format(len(finally_df)))
        self.assertEqual(len(first_df), len(finally_df))

    @Tester(
        ["test_load_data"],
        exec_time=True,
        skip_args_time=True,
    )
    def test_print_only_coord(self):
        """
        测试识别最后的坐标数据
        Example Data: Chroma, TTK(NI TestStand@), STS(NI TestStand@)
        :return:
        """
        df1 = self.df_module.prr_df[["DIE_ID", "X_COORD", "Y_COORD"]].groupby(["X_COORD", "Y_COORD"]).last()
        df = self.df_module.prr_df[self.df_module.prr_df.DIE_ID.isin(df1.DIE_ID)]
        Print.Success("FinallyTestQty:{}".format(len(df)))

    @Tester(
        ["test_load_data"],
        exec_time=True,
        skip_args_time=True,
    )
    def test_calculation_top_fail(self):
        """
        原始数据的top fail计算
        query占用的时间比较长,尽量不要用
        :return:
        """
        self.top_fail_dict = CapabilityUtils.calculation_top_fail(self.li.df_module)
        for key, value in self.top_fail_dict.items():
            print(key, value, sep=" : ")

    @Tester(
        ["test_load_data"],
        exec_time=True,
        skip_args_time=True,
    )
    def test_calculation_new_top_fail(self):
        """
        重新设置limit值后top fail的计算 -> 精度丢失问题, 即使limit没有变化, 算出来的fail rate和上面的函数可能也不一样
        运行时间肯定会长了一些 -> 实际和上面的操作时间一致? 上面的操作应该会更加简单和速度的.
        :return:
        """
        if len(self.li.df_module.prr_df) == 0:
            Print.Danger("No Prr Data!")
            self.assertEqual(1, 0)
        self.top_fail_dict = CapabilityUtils.calculation_new_top_fail(self.li.df_module)
        for key, value in self.top_fail_dict.items():
            print(key, value, sep=" : ")

    @Tester(
        ["test_load_data", "test_calculation_top_fail"],
        exec_time=True,
        skip_args_time=True,
    )
    def test_calculation_capability(self):
        """
        计算制程能力并优化这个逻辑的执行时间, 计算时间保持在 100k row, 200 column -> 1s之内
        数据量到达什么级别就用多线程?
        PTR才做计算
        FTR只统计失效比例( FTR上下限改为 0.9-1.1 )
        :return:
        """
        if len(self.df_module.prr_df) == 0:
            Print.Danger("No Prr Data!")
            self.assertEqual(1, 0)

        if self.top_fail_dict is None:
            print("top fail is None")
            return

        capability_key_list = CapabilityUtils.calculation_capability(self.df_module, self.top_fail_dict)
        Print.print_table(capability_key_list)

        with open(TestVariable.TABLE_PICKLE_PATH, 'wb') as file_obj:
            data_pik = pickle.dumps(capability_key_list)
            file_obj.write(data_pik)

    @Tester(
        ["load_data"],
        exec_time=True,
        skip_args_time=True,
    )
    def test_include_select_to_new_data(self):
        """
        选取想看的测试项目
        :return:
        """

    @Tester(
        ["load_data"],
        exec_time=True,
        skip_args_time=True,
    )
    def test_exclude_select_to_new_data(self):
        """
        选取不想看的项目并删掉
        :return:
        """

    @Tester(
        ["load_data"],
        exec_time=True,
        skip_args_time=True,
    )
    def test_select_and_delete_out_limit_data(self):
        """
        只看选取项目全PASS的数据
        :return:
        """

    @Tester()
    def load_other_data(self):
        return "Data"

    @Tester(
        ["load_data", "load_other_data"],
        exec_time=True,
        skip_args_time=True,
    )
    def test_concat_two_data(self, **kwargs):
        """
        将两个不同程序的相同Device数据链接到一起
        数据结构改变了, 链接起来相对比较困难了
        :return:
        """
        print(kwargs)

    @Tester()
    def load_wat_data(self):
        return "Data"

    @Tester(
        ["load_data", "load_wat_data"],
        exec_time=True,
        skip_args_time=True,
    )
    def test_merge_wat_cp_data(self, **kwargs):
        """
        使用算法将WAT的数据和CP的数据链接
        因为数据结构的改变, 算法套用不那么简单了 -> 主要是速度慢了很多
        :return:
        """
        print(kwargs)

    @Tester(
        ["load_data"],
        exec_time=True,
        skip_args_time=True,
    )
    def test_select_date_unstack(self):
        """
        to csv, jsl read
        unstack的key是 TEST_NUM:TEST_TXT
        """
        pass

    @Tester(
        ["load_data"],
        exec_time=True,
        skip_args_time=True,
    )
    def test_save_data_to_jmp(self):
        """
        to csv, jsl read
        """
        pass


class OldReadHdf5BaseAnalysisCase(unittest.TestCase, Hdf5DataLoad):
    """
    优化解析时间或方法
    """
    li: Li = None

    @Tester(
        ["old_load_data"],
        exec_time=True,
    )
    def test_load_data(self):
        """
        测试Read Hdf5数据
        需要优化项目: concat
        优化方向 -> 数据不要一次就加载完了
            TopFail
            5个文件 -> 40s
            11个文件 -> 107s
            All:
            5个文件 -> 60.168s
            11个文件 -> 107s
        :return:
        """
        # select_summary, id_module_dict = self.summary.load_select_data([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
        select_summary, id_module_dict = self.summary.load_select_data([1, 2])
        self.li.set_data(select_summary, id_module_dict)
        self.li.concat()
        self.li.calculation_top_fail()
        capability_key_list = CapabilityUtils.calculation_capability(self.li.df_module, self.li.top_fail_dict)
        Print.print_table(capability_key_list)


class NewReadHdf5BaseAnalysisCase(unittest.TestCase, Hdf5DataLoad):
    """
    优化解析时间或方法
    """
    li: FutureLi = None

    @Tester(
        ["simple_load_data"],
        exec_time=True,
    )
    def test_pre_view_select_data(self):
        """
        测试Read Hdf5数据
        需要优化项目: concat
        :return:
        """
        ptmd_df = self.summary.pre_view_select_data([2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
        ptmd_df_limit = ParserData.contact_ptmd_list_drop(ptmd_df)
        Print.print_table(CapabilityUtils.pre_view_limit(ptmd_df_limit))

    @Tester(
        ["simple_load_data"],
        exec_time=True,
    )
    def test_load_data(self):
        """
        测试Read Hdf5数据
        需要优化项目: concat
        优化方向 -> 数据不要一次就加载完了
            TopFail
            5个文件 -> 29s
            11个文件 -> 119.077s
        原因->测试项目太多了, 循环的次数太多
            优化:
                1. PreView
                2. Load文件之后修改ID
                3. 减少了TEST_ID
                    TopFail
                    5个文件 -> 20s
                    11个文件 -> 42.453s
                    All:
                    5个文件 -> 26.58s
                    11个文件 -> 42.453s
        :return:
        """
        select_summary, id_module_dict = self.summary.load_select_data([1, 2])
        # select_summary, id_module_dict = self.summary.load_select_data([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
        self.li.set_data(select_summary, id_module_dict)
        unit_group = "WAFER_ID|HDF5_PATH"
        print(self.li.select_summary.info())
        self.li.concat()
        prr_df_list = []
        for key, df in self.li.select_summary.groupby(unit_group.split('|')):
            # print(df.HDF5_PATH)
            temp_df = self.li.df_module.prr_df[self.li.df_module.prr_df.ID.isin(df.ID)]
            print(len(temp_df))
            unit_prr_df = ParserData.get_prr_data(
                temp_df, 4, ReadFail.Y
            )
            print(len(unit_prr_df))
            prr_df_list.append(unit_prr_df)
        self.li.df_module.prr_df = pd.concat(prr_df_list)
        print(self.li.df_module.prr_df)

    @Tester(
        ["test_load_data"],
        exec_time=True,
        skip_args_time=True,
    )
    def test_set_group(self):
        """
        +-----------+-----------+----+--------+----------+----------+--------------+----------+---------+----------+-----------+----------+----------+---------+---------+----------+-------+-------+--------+-----------+-----------+---------------------+-----------+
        | FILE_PATH | FILE_NAME | ID | LOT_ID | SBLOT_ID | WAFER_ID | BLUE_FILM_ID | TEST_COD | FLOW_ID | PART_TYP |  JOB_NAM  | TST_TEMP | NODE_NAM | SETUP_T | START_T | SITE_CNT |  QTY  |  PASS | YIELD  | PART_FLAG | READ_FAIL |      HDF5_PATH      |   GROUP   |
        +-----------+-----------+----+--------+----------+----------+--------------+----------+---------+----------+-----------+----------+----------+---------+---------+----------+-------+-------+--------+-----------+-----------+---------------------+-----------+
        |    DEMO   |    DEMO   | 1  |  DEMO  |   DEMO   |  WAFER   |              |   CP1    |    R0   |  ESP32   | TEST_DEMO |    25    |  Python  |    0    |    0    |    0     | 56963 | 46611 | 81.83% |     0     |     1     | .\test_data\TEST.h5 | DEMO|DEMO |
        +-----------+-----------+----+--------+----------+----------+--------------+----------+---------+----------+-----------+----------+----------+---------+---------+----------+-------+-------+--------+-----------+-----------+---------------------+-----------+

        print(self.li.group_list)
        print(self.li.da_group_list)
        ['DEMO|DEMO']
        ['S012', 'S013', '....']
        :return:
        """

        # print(self.li.df_module.dtp_df.info())
        # capability_list = CapabilityUtils.new_calculation_capability(self.li.df_module)
        # Print.print_table(capability_list)

        self.li.calculation_top_fail()
        self.li.calculation_capability()
        self.li.background_generation_data_use_to_chart_and_to_save_csv()
        self.li.set_data_group(
            ["LOT_ID", "SBLOT_ID"],
            ["SITE_NUM"]
        )
        Print.print_table(self.li.select_summary.to_dict(orient="records"))

    @Tester(
        ["test_load_data"],
        exec_time=True,
        skip_args_time=True,
    )
    def test_cal_pandas_top_fail(self):
        CapabilityUtils.pandas_calculation_capability(self.li.df_module)

    @Tester(
        ["test_set_group"],
        exec_time=True,
        skip_args_time=True,
    )
    def test_trans_bar_plot(self, **kwargs):
        """
        good
        :param kwargs:
        :return:
        """
        app = QApplication.instance()
        if app is None:
            app = QApplication(sys.argv)
        bar_chart = TransBarChart(self.li)
        bar_chart.set_data(22)  # TEST_ID == 1
        bar_chart.set_range_self()
        bar_chart.set_df_chart()
        bar_chart.set_line_self()
        bar_chart.show()
        app.exec_()
